In [1]:
def gcd(a, b):
while b:
a, b = b, a % b
return a
In [2]:
gcd(27, 36)
Out[2]:
In [3]:
gcd(2.7, 3.6)
Out[3]:
In [4]:
gcd('12', '8')
In [75]:
def gcd_2(a, b):
assert isinstance(a, int), 'Expected int'
assert isinstance(b, int), 'Expected int'
while b:
a, b = b, a % b
return a
In [76]:
gcd_2(2.7, 3.6)
In [77]:
gcd_2('12', '8')
In [78]:
class Contract:
@classmethod
def check(cls, value):
pass
class Integer(Contract):
@classmethod
def check(cls, value):
assert isinstance(value, int), 'Expected int'
In [79]:
Integer.check(1)
In [80]:
Integer.check(1.5)
클래스로 만들어 놓은 단순히 반복되는 코드 import
In [5]:
from contract import Contract, Typed, Integer, Float, String, Positive, PositiveInteger
In [6]:
def gcd(a, b):
Integer.check(a)
Positive.check(a)
Integer.check(b)
Positive.check(b)
while b:
a, b = b, a % b
return a
In [7]:
gcd(27, 36)
Out[7]:
In [8]:
gcd("wow", "such")
In [9]:
gcd(-1, 1)
In [10]:
def gcd(a, b):
PositiveInteger.check(a)
PositiveInteger.check(b)
while b:
a, b = b, a % b
return a
In [11]:
gcd(-1, 1)
In [12]:
gcd(27, 36)
Out[12]:
In [13]:
gcd.__annotations__
Out[13]:
In [14]:
from inspect import signature
# inspect 모듈은 파이썬 오브젝트에 대한 정보를 가져옴
# inspect.signature함수는 callable객체의 시그니쳐 정보를 가져옴
In [15]:
signature(gcd)
Out[15]:
In [16]:
signature(gcd).bind(1, 4)
Out[16]:
In [17]:
signature(gcd).bind(1, 4).arguments
Out[17]:
In [18]:
from contract import checked
In [19]:
@checked
def gcd(a: PositiveInteger, b: PositiveInteger):
while b:
a, b = b, a % b
return a
In [20]:
gcd(11, 22)
Out[20]:
In [21]:
gcd(-11, 22)
In [22]:
class Player:
def __init__(self, name, x, y):
self.name = name
self.x = x
self.y = y
def left(self, dx):
self.x -= dx
def right(self, dx):
self.x += dx
In [23]:
p = Player('아드', 10, 2)
p.x
Out[23]:
In [24]:
p.left(-5)
In [25]:
p.x
Out[25]:
In [26]:
p.x = '하핳 받아랏!!'
p.left(-5)
위와 같은 문제를 방지하기 위하여 getter/setter 즉, property를 사용
In [27]:
class Player:
def __init__(self, name, x, y):
self.name = name
self.x = x
self.y = y
@property
def x(self):
return self._x
@x.setter
def x(self, value):
Integer.check(value)
self._x = value
In [28]:
p = Player('아드', 0, 0)
p.x = 10
In [29]:
p.x = '멍츙'
그러나 일일히 프로퍼티를 만드는 대신 클래스를 사용할 수 있음
In [30]:
# 수정 전
class Contract:
@classmethod
def check(cls, value):
pass
In [31]:
# 수정 후
class Contract:
def __set__(self, instance, value):
self.check(value)
instance.__dict__[self.name] = value
def __set_name__(self, owner, name):
self.name = name
@classmethod
def check(cls, value):
pass
Player 클래스는 프로퍼티 제거
In [32]:
from contract import NonEmpty, NonEmptyString
In [33]:
class Player:
name = NonEmptyString()
x = Integer()
y = Integer()
def __init__(self, name, x, y):
self.name = name
self.x = x
self.y = y
In [34]:
p = Player('아드', 0, 0)
In [35]:
p.name = 10
In [36]:
p.name = ''
In [37]:
class Player:
name: NonEmptyString
x: Integer
y: Integer
In [38]:
Player.__annotations__
Out[38]:
In [39]:
class Base:
@classmethod
def __init_subclass__(cls):
for name, val in cls.__annotations__.items():
contract = val()
contract.__set_name__(cls, name)
setattr(cls, name, contract)
class Player(Base):
name: NonEmptyString
x: Integer
y: Integer
def __init__(self, name, x, y):
self.name = name
self.x = x
self.y = y
In [40]:
p = Player('아드', 0, 0)
p.name = 12
In [41]:
class Base:
@classmethod
def __init_subclass__(cls):
for name, val in cls.__annotations__.items():
contract = val()
contract.__set_name__(cls, name)
setattr(cls, name, contract)
def __init__(self, *args):
ann = self.__annotations__
assert len(ann) == len(args), f'Expected {len(ann)} arguments'
for name, val in zip(ann, args):
setattr(self, name, val)
def __repr__(self):
args = ', '.join(repr(getattr(self, name)) for name in self.__annotations__)
return f'{type(self).__name__}({args})'
class Player(Base):
name: NonEmptyString
x: Integer
y: Integer
def left(self, dx):
self.x -= dx
def right(self, dx):
self.x += dx
In [42]:
p = Player('아드', 0, 0)
p
Out[42]:
In [43]:
p.x
Out[43]:
In [44]:
p.x='코드엑스'
In [46]:
def chcked(func):
sig = signature(func)
ann = func.__annotations__
@wraps(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
for name, val in bound.arguments.items():
if name in ann:
ann[name].check(val)
return func(*args, **kwargs)
return wrapper
class Base:
@classmethod
def __init_subclass__(cls):
for name, val in cls.__dict__.items():
if callable(val):
setattr(cls, name, chcked(val))
for name, val in cls.__annotations__.items():
contract = val()
contract.__set_name__(cls, name)
setattr(cls, name, contract)
def __init__(self, *args):
ann = self.__annotations__
assert len(ann) == len(args), f'Expected {len(ann)} arguments'
for name, val in zip(ann, args):
setattr(self, name, val)
def __repr__(self):
args = ', '.join(repr(getattr(self, name)) for name in self.__annotations__)
return f'{type(self).__name__}({args})'
class Player(Base):
name: NonEmptyString
x: Integer
y: Integer
def left(self, dx: PositiveInteger):
self.x -= dx
def right(self, dx: PositiveInteger):
self.x += dx
In [47]:
p = Player('아드', 0, 0)
In [48]:
p
Out[48]:
In [49]:
p.left(0)
In [50]:
from collections import OrderedDict
dic = {}
class Meta(type):
@classmethod
def __prepare__(cls, *args):
# __prepare__는 클래스의 어트리뷰트와 매서드들을 키와 벨류로 가지는 딕셔너리를 리턴
global dic
dic = OrderedDict()
return dic
class Base(metaclass=Meta):
a = 'wow'
def __init__(self):
self.name = '아드'
self.x = 0
self.y = 42
print(dic)
In [51]:
class Meta(type):
@classmethod
def __prepare__(cls, *args):
def see_me():
return '내가 보이니..?'
return {'see_me': see_me}
class Base(metaclass=Meta):
a = see_me()
print(Base.a)
In [52]:
class Meta(type):
@classmethod
def __prepare__(cls, *args):
return {}
def __new__(meta, name, bases, methods):
print(f'name : "{name}", methods: {methods}')
return super().__new__(meta, name, bases, methods)
class Base(metaclass=Meta):
pass
In [53]:
class Meta(type):
@classmethod
def __prepare__(cls, *args):
def see_me():
return '내가 보이니..?'
return {'see_me': see_me}
def __new__(meta, name, bases, methods):
return super().__new__(meta, name, bases, methods)
class Base(metaclass=Meta):
pass
print(Base.see_me())
클래스 외부에서 어트리뷰트로 see_me에 접근할 수 있는 문제를 해결하기 위하여 collections.ChainMap 사용
In [55]:
from collections import ChainMap
In [56]:
dic = None
class Meta(type):
@classmethod
def __prepare__(cls, *args):
def see_me():
return '내가 보이니..?'
global dic
dic = ChainMap({}, {'see_me': see_me})
return dic
def __new__(meta, name, bases, methods):
methods = methods.maps[0]
return super().__new__(meta, name, bases, methods)
class Base(metaclass=Meta):
a = see_me()
print(Base.a)
print('see_me' in Base.__dict__.keys())
chainmap 예시
In [57]:
c = ChainMap({}, {'x': 0, 'y': 0})
c['x']
Out[57]:
In [58]:
c['a'] = 42
c
Out[58]:
In [59]:
c.maps[0]
Out[59]:
In [60]:
c['y']
Out[60]:
In [68]:
class BaseMeta(type):
@classmethod
def __prepare__(cls, *args):
return ChainMap({}, _contracts)
def __new__(meta, name, bases, methods):
methods = methods.maps[0]
return super().__new__(meta, name, bases, methods)
In [72]:
def chcked(func):
sig = signature(func)
ann = func.__annotations__
@wraps(func)
def wrapper(*args, **kwargs):
bound = sig.bind(*args, **kwargs)
for name, val in bound.arguments.items():
if name in ann:
ann[name].check(val)
return func(*args, **kwargs)
return wrapper
class Base:
@classmethod
def __init_subclass__(cls):
for name, val in cls.__dict__.items():
if callable(val):
setattr(cls, name, chcked(val))
for name, val in cls.__annotations__.items():
contract = val()
contract.__set_name__(cls, name)
setattr(cls, name, contract)
def __init__(self, *args):
ann = self.__annotations__
assert len(ann) == len(args), f'Expected {len(ann)} arguments'
for name, val in zip(ann, args):
setattr(self, name, val)
def __repr__(self):
args = ', '.join(repr(getattr(self, name)) for name in self.__annotations__)
return f'{type(self).__name__}({args})'
class Player(Base):
name: NonEmptyString
x: Integer
y: Integer
def left(self, dx: PositiveInteger):
self.x -= dx
def right(self, dx: PositiveInteger):
self.x += dx
In [73]:
p = Player('아드', 0, 0)
p.left(-1)
In [74]:
from contract import Base, PositiveInteger
dx: PositiveInteger
class Player(Base):
name: NonEmptyString
x: Integer
y: Integer
def left(self, dx):
self.x -= dx
def right(self, dx):
self.x += dx
p = Player('아드', 0, 0)
p.left(-1)